package org.jheuristics.ga.operators.selectors;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.epanetgrid.otimizacao.ag.ViaveisSelector;
import org.epanetgrid.otimizacao.log.AGLogger;
import org.jheuristics.Individual;
import org.jheuristics.ga.DefaultPopulation;
import org.jheuristics.ga.GAConfig;
import org.jheuristics.ga.GAStatus;
import org.jheuristics.ga.Population;
import org.jheuristics.ga.multiObjective.DistanceAlgorithm;


/**
 * TODO
 *
 * @author Marcell Manfrin, marcell@ourgrid.org, Oct 27, 2005
 * @author C�cero Alan, alan@ourgrid.org, Oct 27, 2005
 */
public class CutSelector extends AbstractDistanceSelector {

	/**
	 * TODO
	 *
	 * @param densityAlgorithm
	 */
	public CutSelector(DistanceAlgorithm densityAlgorithm) {
		super(densityAlgorithm);
	}

	/**
	 * TODO
	 *
	 * @param individuals
	 * @param howMany
	 * @param status
	 * @param config
	 * @return
	 * @see org.jheuristics.ga.operators.selectors.AbstractDistanceSelector#select(org.jheuristics.Individual[], int, org.jheuristics.ga.GAStatus, org.jheuristics.ga.GAConfig)
	 */
	public Individual[] select(Individual[] individuals, int howMany, GAStatus status, GAConfig config) {
		
//		if(!contemApenasViaveisNaoDominados(individuals)) {
//			return individuals;
//		}
		
		StringBuffer buffer = new StringBuffer();
		
		Population result = new DefaultPopulation();
		result.addIndividuals(individuals);
		for (int i = 0, removeCount = result.size() - howMany; i < removeCount; i++) {
			buffer.append("Itera��o ");
			buffer.append(i+1);
			buffer.append(":");
			List[] distances = getDistanceAlgorithm().getDistances(result.toIndividualArray(), status, config);
			// TODO: Testando corte
//			int index = removeIndividual(distances);
//			Individual cortado = result.getIndividual(index);
//			SPMultiObjectiveFitness fitness = (SPMultiObjectiveFitness) cortado.getFitness();
//			System.out.println(new BigInteger(OtimizacaoUtils.gensToString((List) cortado.getGens()), 2).add(BigInteger.ONE).toString() + "\t" + fitness.getRawFitness() + "\t" + fitness.getDensity());
			// added (Marcell)
			buffer.append("\n");
			buffer.append("Dist�ncias: ");
			buffer.append("\n");
			buffer.append(getStringLinhas(distances));
			buffer.append("\n");
			int posicao = removeIndividual(distances);
			buffer.append("Indiv�duo Cortado: ");
			buffer.append(result.getIndividual(posicao));
			buffer.append("\n");
			result.removeIndividual(posicao);
		}
		buffer.append("====================================================================================================\n");
		
		AGLogger.getInstance().debug(buffer);
		
		return result.toIndividualArray();
	}
	
	/**
	 * Retorna se o array contem apenas individuos n�o dominados vi�veis
	 * @param individuals
	 * @return
	 */
	private boolean contemApenasViaveisNaoDominados(Individual[] individuals) {
		
		Population pop1 = new DefaultPopulation();
		
		pop1.addIndividuals(individuals);
		
		Individual[] viaveis = new ViaveisSelector().select(pop1, 0, null, null);

		Population popViaveis = new DefaultPopulation();
		
		popViaveis.addIndividuals(viaveis);
		
		Individual[] viaveisNaoDominados = new NonDominatedSelector().select(popViaveis, 0, null, null);
		
		return individuals.length == viaveisNaoDominados.length;
	}

	private String getStringLinhas(List[] linhas) {
		String stringFinal = "";
		for (List strings : linhas) {
			for (int i = 0; i < strings.size(); i++) {
				stringFinal= stringFinal+" "+format(strings.get(i).toString(), 10);
			}
			stringFinal = stringFinal+ "+\n";
		}
		return stringFinal;
	}
	
	private String format(String str, int num) {
		String result = new String(str);
		for(int i = 0; i < num && result.length() < num; i++) {
			result+=" ";
		}
		return result;
	}

	/**
	 * TODO
	 *
	 * @param distances
	 * @return
	 */
	private int removeIndividual(List[] distances) {
		for (int i = 0; i < distances.length; i++) {
			Collections.sort(distances[i]);
		}
		int index = 0;
		double min = ((Double) distances[index].get(0)).doubleValue();
		for (int i = 1; i < distances.length; i++) {
			double value = ((Double) distances[i].get(0)).doubleValue();
			if (value < min) {
				index = i;
				min = value;
			} else if (value == min) {
				List distance1 = distances[index];
				List distance2 = distances[i];
				for (Iterator it1 = distance1.iterator(), it2 = distance2.iterator(); it1.hasNext() && it2.hasNext(); ) {
					double value1 = ((Double) it1.next()).doubleValue();
					double value2 = ((Double) it2.next()).doubleValue();
					if (value1 != value2) {
						if (value2 < value1) {
							index = i;
							min = value;
						}
						break;
					}
				}
				min = ((Double) distances[index].get(0)).doubleValue();
			}
		}
		return index;
	}

	@Override
	public Individual[] select(Population population, int howMany, GAStatus status, GAConfig config) {
		
		if (null == population) {
			throw new NullPointerException();
		}
		if (0 > howMany) {
			throw new IllegalArgumentException();
		}
		if (0 == howMany) {
			return new Individual[0];
		}
		
		
		// TODO Auto-generated method stub
		StringBuffer buffer = new StringBuffer("\n============= Algoritmo de corte==========================================================================\n");
		
		buffer.append("Indiv�duos:\n");
		for (Individual ind : population.toIndividualArray()) {
			buffer.append(ind);
			buffer.append("\n");
		}
		
		AGLogger.getInstance().debug(buffer);
		
		return super.select(population, howMany, status, config);
	}

}
